package org.etherframework.tests.features.dependency
{
	import org.etherframework.core.IModule;
	import org.etherframework.dependency.IDependencies;
	import org.etherframework.tests.features.dependency.models.ComplexModel;
	import org.etherframework.tests.features.dependency.models.IInstanceModel2;
	import org.etherframework.tests.features.dependency.models.IInterfacedModel;
	import org.etherframework.tests.features.dependency.models.InstanceModel;
	import org.etherframework.tests.features.dependency.models.InstanceModel2;
	import org.etherframework.tests.features.dependency.models.InterfacedModel;
	import org.etherframework.tests.features.dependency.models.NoneExistentModel;
	import org.etherframework.tests.features.dependency.models.RecursiveModel;
	import org.etherframework.tests.features.dependency.models.SimpleModel;
	import org.etherframework.tests.features.dependency.models.SingletonModel;
	import org.etherframework.tests.utils.TestUtility;
	import org.hamcrest.assertThat;
	import org.hamcrest.core.not;
	import org.hamcrest.object.equalTo;
	import org.hamcrest.object.notNullValue;

	public class when_instantiating_dependencies
	{	
		private static var _testUtility:TestUtility = new TestUtility();
		private static var _module:IModule;
		private static var _d:IDependencies;
		
		private static var _simpleModel:SimpleModel;
		private static var _complexModel:ComplexModel;
		private static var _interfacedModel:InterfacedModel;
		private static var _singletonModel:SingletonModel;
		private static var _instanceModel:InstanceModel;
		private static var _instanceModel2:InstanceModel2;
		
		[BeforeClass]
		public static function setUpBeforeClass():void
		{
			_module = _testUtility.createModule();
			_d = _module.dependencies;
			_d.addClass(SimpleModel);
			_d.addClass(ComplexModel);
			_d.addClass(InterfacedModel, IInterfacedModel);
			_d.addSingleton(SingletonModel);
			_d.addInstance(new InstanceModel());
			_d.addInstance(new InstanceModel2(), IInstanceModel2);
		}
		
		[AfterClass]
		public static function tearDownAfterClass():void
		{
			_testUtility.dispose();
			SimpleModel.instanceCounter = 0;
			ComplexModel.instanceCounter = 0;
			InterfacedModel.instanceCounter = 0;
			SingletonModel.instanceCounter = 0;
			InstanceModel.instanceCounter = 0;
			InstanceModel2.instanceCounter = 0;
		}
		
		[Before]
		public function setUp():void
		{
		}
		
		[After]
		public function tearDown():void
		{
		}
		
		[Test(order=2)]
		public function dependencies_have_valid_instancies():void
		{
			_simpleModel = _d.getDependency(SimpleModel).getInstance() as SimpleModel
			_complexModel = _d.getDependency(ComplexModel).getInstance() as ComplexModel;
			_interfacedModel = _d.getDependency(IInterfacedModel).getInstance() as InterfacedModel;
			_singletonModel = _d.getDependency(SingletonModel).getInstance() as SingletonModel;
			_instanceModel = _d.getDependency(InstanceModel).getInstance() as InstanceModel;
			_instanceModel2 = _d.getDependency(IInstanceModel2).getInstance() as InstanceModel2;
			
			assertThat(_simpleModel, notNullValue());
			assertThat(_complexModel, notNullValue());
			assertThat(_interfacedModel, notNullValue());
			assertThat(_singletonModel, notNullValue());
			assertThat(_instanceModel, notNullValue());
			assertThat(_instanceModel2, notNullValue());
		}
		
		[Test(order=3)]
		public function dependencies_instantiated_correct_number_of_times():void
		{
			assertThat(SimpleModel.instanceCounter, equalTo(5));
			assertThat(ComplexModel.instanceCounter, equalTo(1));
			assertThat(InterfacedModel.instanceCounter, equalTo(3));
			assertThat(SingletonModel.instanceCounter, equalTo(1));
			assertThat(InstanceModel.instanceCounter, equalTo(1));
			assertThat(InstanceModel2.instanceCounter, equalTo(1));
		}
		
		[Test(order=4)]
		public function dependencies_instantiated_with_correct_constructor_arguemnts():void
		{
			assertThat(_complexModel.simpleModel, notNullValue());
			assertThat(_complexModel.interfacedModel, notNullValue());
			assertThat(_complexModel.singletonModel, notNullValue());
			assertThat(_interfacedModel.simpleModel, notNullValue());
			assertThat(_singletonModel.interfacedModel, notNullValue());
			
			assertThat(_complexModel.simpleModel, not(equalTo(_simpleModel)));
			assertThat(_complexModel.interfacedModel, not(equalTo(_interfacedModel)));
			assertThat(_complexModel.singletonModel, equalTo(_singletonModel));
			assertThat(_complexModel.instanceModel, equalTo(_instanceModel));
			assertThat(_complexModel.instanceModel2, equalTo(_instanceModel2));
			assertThat(_interfacedModel.simpleModel, not(equalTo(_simpleModel)));
			assertThat(_singletonModel.interfacedModel, not(equalTo(_interfacedModel)));
		}
		
		[Test(order=5)]
		public function dependencies_should_have_ether():void
		{
			assertThat(_simpleModel.ether, notNullValue());
			assertThat(_complexModel.ether, notNullValue());
			assertThat(_interfacedModel.ether, notNullValue());
			assertThat(_singletonModel.ether, notNullValue());
		}
		
		[Test(order=6)]
		public function it_should_throw_an_error_when_injection_is_recursive():void
		{
			var err:Error;
			_d.addClass(RecursiveModel);
			// This test can fail with error in debug mode. 
			try {
				_d.getDependency(RecursiveModel).getInstance();
			} catch (e:*) {err = e;}
			
			assertThat(err, notNullValue());
		}
		
		[Test(order=7)]
		public function it_should_throw_an_error_when_trying_to_get_nonexistent_type():void
		{
			var err:Error;
			
			try {
				_d.getDependency(NoneExistentModel).getInstance();
			} catch (e:*) {err = e;}
			
			assertThat(err, notNullValue());
		}
		
	}
}